#include "AdvancedShowDesktop.h"

///Copyright (c) 2009-2010 T. Klepzig 

AdvancedShowDesktop* AdvancedShowDesktop::instance = 0;

HWINEVENTHOOK AdvancedShowDesktop::hWinEventHook = 0;
HHOOK AdvancedShowDesktop::hKeyboardHook = 0;

bool AdvancedShowDesktop::shortcutDown=false;


AdvancedShowDesktop::AdvancedShowDesktop(HINSTANCE hInstance)
{	
	instance = this;
	this->hInstance = hInstance;
	windowsHidden = false;
	windowVisibilityHelper = new WindowVisibilityHelper(hInstance, false);
}

AdvancedShowDesktop::~AdvancedShowDesktop()
{
	Deactivate();

	instance = 0;
	delete windowVisibilityHelper;
	windowVisibilityHelper = 0;
}

AdvancedShowDesktop* AdvancedShowDesktop::CreateInstance(HINSTANCE hInstance)
{
	if (instance)
		return 0;

	return new AdvancedShowDesktop(hInstance);
}


CCState AdvancedShowDesktop::Activate()
{
	CCState result;
	if ((result = CassiopeiaComponent::Activate()) != CCState::Successful)
		return result;

	hWinEventHook=SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_MINIMIZEEND, 0, WinEventProc, 0, 0, 0);
	hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL,KeyboardHookProc,hInstance,0);

	return CCState::Successful;
}

CCState AdvancedShowDesktop::Deactivate()
{
	CCState result;
	if ((result = CassiopeiaComponent::Deactivate()) != CCState::Successful)
		return result;

	UnhookWinEvent(hWinEventHook);
	UnhookWindowsHookEx(hKeyboardHook);

	RestoreWindows();

	return CCState::Successful;
}

void AdvancedShowDesktop::ShowDesktop()
{
	currentMonitor = MonitorFromPoint(Utilities::GetCursorPosition(),MONITOR_DEFAULTTOPRIMARY);
	
	if (windowsHidden)
		return;

	EnumWindows(EnumWindowsProc, 0);
	SetForegroundWindow(GetShellWindow());
	windowsHidden=true;
}

void AdvancedShowDesktop::RestoreWindows()
{	
	if (!windowsHidden)
		return;

	if (hiddenHWnds.empty())
	{
		windowsHidden = false;
		return;
	}

#pragma warning hier treten manchmal Fehler auf: "iterator not incrementable"...

	set<HWND>::iterator iter;
	for(iter = hiddenHWnds.begin(); iter != hiddenHWnds.end(); ++iter)
	{
		windowVisibilityHelper->Show(*iter);
	}
	hiddenHWnds.clear();

	windowsHidden = false;
}

void AdvancedShowDesktop::ToggleDesktop(bool forceRestore)
{
	if (forceRestore)
	{
		RestoreWindows();
		return;
	}

	static HWND foregroundHWnd = 0;
	if (!windowsHidden)
	{
		foregroundHWnd = GetForegroundWindow();
		ShowDesktop();
	}
	else
	{
		RestoreWindows();
		SetForegroundWindow(foregroundHWnd);
	}
}


BOOL CALLBACK AdvancedShowDesktop::EnumWindowsProc(HWND hWnd, LPARAM lParam)
{
	TCHAR className[255]={0};
	GetClassName(hWnd,className,sizeof(className));

	if (IsWindow(hWnd)
		&& IsWindowVisible(hWnd)
		&& !IsIconic(hWnd)
		&& !instance->windowVisibilityHelper->IsHidden(hWnd)
		&& !Utilities::IsSpecialWindow(hWnd)		
		&& !Utilities::IsSpecialCassiopeiaWindow(hWnd)		
		&& MonitorFromWindow(hWnd, MONITOR_DEFAULTTOPRIMARY) == instance->currentMonitor)
	{
			instance->windowVisibilityHelper->Hide(hWnd);
			instance->hiddenHWnds.insert(hWnd);		
	}

	return true;
}

LRESULT CALLBACK AdvancedShowDesktop::KeyboardHookProc(int code,WPARAM wParam,LPARAM lParam)
{
	bool cancel=false;

	if (wParam == WM_KEYDOWN)
	{
		KBDLLHOOKSTRUCT* p = (KBDLLHOOKSTRUCT*)lParam;

		if (p->vkCode == 68 && (GetAsyncKeyState(VK_LWIN) & 0x8000) == 0x8000)
		{
			if (!shortcutDown)
				instance->ToggleDesktop();
			shortcutDown = true;
			cancel = true;
		}
	}
	else if (wParam == WM_KEYUP)
	{		
		KBDLLHOOKSTRUCT* p = (KBDLLHOOKSTRUCT*)lParam;

		if (shortcutDown)
		{
			if (p->vkCode == VK_LWIN)
			{
				keybd_event(68, 0, KEYEVENTF_KEYUP, 0);
				shortcutDown = false;
			}
			else if (p->vkCode == 68)
			{
				shortcutDown = false;
			}
		}
	}

	return (cancel)?1L:CallNextHookEx(hKeyboardHook,code,wParam,lParam);
}

VOID CALLBACK AdvancedShowDesktop::WinEventProc(HWINEVENTHOOK hWinEventHook,DWORD dwEvent,HWND hWnd,LONG idObject,LONG idChild,DWORD idEventThread,DWORD dwmsEventTime)
{
	if ((dwEvent == EVENT_SYSTEM_FOREGROUND || dwEvent == EVENT_SYSTEM_MINIMIZEEND) && idObject == OBJID_WINDOW)
	{
		TCHAR className[255]={0};
		GetClassName(hWnd,className,sizeof(className));

		if (!IsIconic(hWnd)
			&& hWnd != Utilities::GetDesktopWindow(Utilities::DW_SHELLDLL_DefViewParent)
			&& hWnd != GetShellWindow()
			&& !Utilities::IsSpecialCassiopeiaWindow(hWnd))
		{
			instance->RestoreWindows();
		}
	}
}
